package com.ragdollcat.thirdaicodegenerate.ai;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.ragdollcat.thirdaicodegenerate.ai.tool.*;
import com.ragdollcat.thirdaicodegenerate.enums.CodeGenTypeEnum;
import com.ragdollcat.thirdaicodegenerate.guradrail.PromptSafetyInputGuardrail;
import com.ragdollcat.thirdaicodegenerate.service.ChatHistoryService;
import com.ragdollcat.thirdaicodegenerate.util.SpringBeanUtil;
import dev.langchain4j.community.store.memory.chat.redis.RedisChatMemoryStore;
import dev.langchain4j.data.message.ToolExecutionResultMessage;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.service.AiServices;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class AiCodeGeneratorFactory {

//    /**
//     * 引入对话记忆
//     */
//    @Resource
//    private RedisChatMemoryStore redisChatMemoryStore;

    @Resource
    @Lazy
    private ChatHistoryService chatHistoryService;

    @Resource
    private ToolManager toolManager;


    /**
     * 加入缓存，key appId_应用类型,value 对应的AiCodeGeneratorService
     * 最大缓存1000个实例
     * 写入后30分钟清理
     * 访问后10分钟清理
     *
     * @return
     */
    private final Cache<String, AiCodeGeneratorService> chatMemoryCache = Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(30, TimeUnit.MINUTES)
            .expireAfterAccess(10, TimeUnit.MINUTES)
            .removalListener(((key, value, cause) -> {
                log.info("removed key: {}, value: {},cause:{}", key, value, cause);
            })).build();

    /**
     * 该方法废弃
     *
     * @return
     */
    @Bean
    @Deprecated
    public AiCodeGeneratorService aiCodeGeneratorService() {
        return visitCache(0L, CodeGenTypeEnum.HTML.getValue());
    }

    /**
     * 每个实例对应一个对话记忆，用appId进行区分
     *
     * @param appId
     * @return
     */
    public AiCodeGeneratorService aiCodeGeneratorService(long appId, String bizType) {
        RedisChatMemoryStore chatMemoryStore = RedisChatMemoryStore.builder().host("localhost").port(6379).prefix(appId + ":").build();

        //初始化对话记忆
        MessageWindowChatMemory messageWindowChatMemory =
                new MessageWindowChatMemory.Builder().id(appId).chatMemoryStore(chatMemoryStore).maxMessages(100).build();


        //从数据库加载对话历史到记忆中
        //在方法中，先清理messageWindowChatMemory中的历史值。
        // 这里为什么要清除？假设我有appId为1的，那么已经构建过了appId为1的MessageWindowChatMemory。
        //能走到这一步，除了第一次创建appId为1的caffine缓存，还有可能是之前的缓存到期了。
        //那我第一次用appId为1的创建了一个MessageWindowChatMemory，
        //第二次依旧用appId为1的创建了一个MessageWindowChatMemory，
        //这两个MessageWindowChatMemory都不是同一个实例，为什么第二个MessageWindowChatMemory会有第一个的记忆？
        //todo 因为它们共享了同一个redisChatMemoryStore，导致底层存储的数据被复用。
//        chatHistoryService.ChatHisD2Memory(appId, messageWindowChatMemory, 20);

        //根据不同的bizType，选择不同的模型
        return switch (bizType) {
            case "html", "multi_file" -> {
                //从Spring bean工厂中获取多例的普通流式模型
                StreamingChatModel commonStreamingChatModel = SpringBeanUtil.getBean("commonStreamingChatModel", StreamingChatModel.class);
                ChatModel commonChatModel = SpringBeanUtil.getBean("commonChatModel", ChatModel.class);
                yield AiServices.builder(AiCodeGeneratorService.class)
                        .chatModel(commonChatModel)
                        .streamingChatModel(commonStreamingChatModel)
                        .chatMemory(messageWindowChatMemory)
                        .inputGuardrails(new PromptSafetyInputGuardrail())
                        .build();
            }
            case "vue_project" -> {
                //从Spring bean工厂中获取多例的推理流式模型
                StreamingChatModel reasoningStreamingChatModel = SpringBeanUtil.getBean("reasoningStreamingChatModel", StreamingChatModel.class);
                yield AiServices.builder(AiCodeGeneratorService.class)
                        .streamingChatModel(reasoningStreamingChatModel)
                        .chatMemoryProvider(memoryId -> messageWindowChatMemory).
                        tools(toolManager.getBaseTool())
                        .inputGuardrails(new PromptSafetyInputGuardrail())
                        .hallucinatedToolNameStrategy(toolExecutionRequest -> ToolExecutionResultMessage.from(
                                toolExecutionRequest, "Error:there is no tool called:" + toolExecutionRequest.name()
                        )).build();
            }
            default -> null;
        };

    }

    /**
     * 做缓存，逻辑是 首先从缓存中查询，如果能查询到某个appId对应的AiCodeGeneratorService，就直接返回
     * 否则创建,走aiCodeGeneratorService方法
     * todo 注意，这里缓存的是某个appId对应的AiCodeGeneratorService实例
     */
    public AiCodeGeneratorService visitCache(long appId, String bizType) {
        String key = makeKey(appId, bizType);
        return chatMemoryCache.get(key, k -> aiCodeGeneratorService(appId, bizType));
    }

    /**
     * appId要拼接上bizType
     *
     * @param appId
     * @param bizType
     * @return
     */
    private String makeKey(long appId, String bizType) {
        return appId + "_" + bizType;
    }


}
